﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObviousCode.Aspect.PluginLibrary.Interfaces;
using ObviousCode.Common.Utils.Library.Collections;
using ObviousCode.Aspect.PluginLibrary.Args;
using System.Drawing;
using ObviousCode.Common.Utils;
using ObviousCode.Common.Utils.Library.Dictionaries;
using ObviousCode.Common.Utils.Library.Exceptions;

namespace ObviousCode.Aspect.PluginLibrary.StateHandler
{
    public class BasicStateHandler : IStateHandler
    {
		// Fields (4) 

        bool _isInAffinativeDrag;
        bool _mouseDownOn;
        string _reservedState;
        string _state;

		// Constructors (1) 

        public BasicStateHandler()
        {
            _mouseDownOn = false;
            _isInAffinativeDrag = false;            
        
            State = BaseItemState.Unselected;
            _reservedState = null;
        }

		// Properties (11) 

        public PointF CurrentDragPosition { get; set; }

        public PointF DragStartPoint { get; set; }

        public bool HandlesDrag { get { return true; } }

        public bool HandlesHover { get { return true; } }

        public bool HandlesSelect { get { return true; } }

        public bool IsInAffinativeDrag { get { return _isInAffinativeDrag; } }

        public PointF LastMouseDownOffset { get; set; }

        public PointF LastMouseDownPoint { get; set; }

        public RectangleF LastRenderedBounds { get; set; }

        public PointF Position { get; private set; }

        public string State 
        {
            get
            {
                return _state;
            }
            private set
            {
                bool selectionChange = 
                    _state == BaseItemState.Selected ||
                    value == BaseItemState.Selected;                

                _state = value;

                if (StateChanged != null)
                {
                    StateChanged(this, new EventArgs<string>(_state));
                }

                if (selectionChange &&
                    SelectionStateChanged != null
                    )
                {
                    SelectionStateChanged(this, EventArgs.Empty);
                }
            }
        }

		// Delegates and Events (5) 

		// Events (5) 

        public event EventHandler DragCompleted;

        public event EventHandler<EventArgs<PointF>> Dragging;

        public event EventHandler DragStarted;

        public event EventHandler SelectionStateChanged;

        public event EventHandler<EventArgs<string>> StateChanged;

		// Methods (14) 

		// Public Methods (8) 

         public void BeginAffinativeDrag()
        {
            if (!HandlesDrag) return;

            _isInAffinativeDrag = true;
            BeginDrag();
        }

        public void ContinueAffinativeDrag(PointF pointF)
        {
            if (!HandlesDrag || !_isInAffinativeDrag) return;

            ContinueDrag(pointF);
        }

        public void EndAffinativeDrag()
        {
            if (!HandlesDrag || !_isInAffinativeDrag) return;

            EndDrag();

            _isInAffinativeDrag = false;
        }

        public object OnMouseDownStateHandler(params object[] args)
        {            
            Parameters p = Parameters.Load(args);
            MousePluginEventArgs m = p.Get<MousePluginEventArgs>(0);

            LastMouseDownPoint = m.MousePosition;
            LastMouseDownOffset = new PointF(LastMouseDownPoint.X - Position.X, LastMouseDownPoint.Y - Position.Y);

            if (m.IsInBounds)
            {
                _mouseDownOn = true;
                if (State == BaseItemState.Selected)
                {
                    _reservedState = BaseItemState.Hovered;
                    
                    return AspectConstants.EventResponse_Null;
                }
                else
                {
                    State = BaseItemState.Selected;
                    
                    return AspectConstants.EventResponse_Repaint;
                }

            }
            else
            {
                if (State == BaseItemState.Selected)
                {
                    if (!m.ShiftKey && !m.ControlKey)
                    {
                        if (m.MultipleSelection)
                        {
                            _reservedState = BaseItemState.Unselected;
                        }
                        else
                        {
                            State = BaseItemState.Unselected;
                            return AspectConstants.EventResponse_Repaint;
                        }
                    }
                }
            }

            return AspectConstants.EventResponse_Null;
        }

        public object OnMouseMoveStateHandler(params object[]  args)
        {
            Parameters p = Parameters.Load(args);            
            MousePluginEventArgs m = p.Get<MousePluginEventArgs>(0);

            if (State == BaseItemState.Dragged)
            {
                ContinueDrag(m.MousePosition);

                return AspectConstants.EventResponse_Repaint;// Bounds;
            }
            else if (m.IsLasooing)
            {
                return HandledLasooMouseMove(m);
            }
            else
            {
                return HandleNonLassoMouseMove(m);
            }            
        }

        public object OnMouseUpStateHandler(params object[] args)
        {
            try
            {
                Parameters p = Parameters.Load(args);
                MousePluginEventArgs m = p.Get<MousePluginEventArgs>(0);

                if (!string.IsNullOrEmpty(_reservedState))
                {
                    State = _reservedState;
                    _reservedState = null;
                    return AspectConstants.EventResponse_Repaint;
                }


                if (State == BaseItemState.Dragged)
                {
                    EndDrag();
                    return AspectConstants.EventResponse_Repaint;
                }
                else
                {

                }

                return AspectConstants.EventResponse_Null;
            }
            finally
            {
                _mouseDownOn = false;
                _reservedState = null;
            }
        }

        public bool RequestState(string state)
        {
            State = state;
            return true;
        }

        public void SetPosition(PointF point)
        {
            Position = point;
        }
		// Private Methods (6) 

        private void BeginDrag()
        {
            State = BaseItemState.Dragged;
            _reservedState = null;

            DragStartPoint = LastMouseDownPoint;

            if (DragStarted != null && !_isInAffinativeDrag) 
            {
                DragStarted(this, EventArgs.Empty);
            }
        }

        private void ContinueDrag(PointF mousePoint)
        {
            CurrentDragPosition = new PointF(
                DragStartPoint.X + (mousePoint.X - DragStartPoint.X) - LastMouseDownOffset.X,
                DragStartPoint.Y + (mousePoint.Y - DragStartPoint.Y) - LastMouseDownOffset.Y
                );

            if (Dragging != null && !_isInAffinativeDrag)
            {
                Dragging(this, new EventArgs<PointF>(mousePoint));
            }
        }

        private void EndDrag()
        {
            Position = new Point((int)CurrentDragPosition.X, (int)CurrentDragPosition.Y);

            State = BaseItemState.Selected;

            if (DragCompleted != null && !_isInAffinativeDrag)
            {
                DragCompleted(this, EventArgs.Empty);
            }
        }

        private object HandledLasooMouseMove(MousePluginEventArgs m)
        {
            _reservedState = null;

            State = m.LasooBounds.IntersectsWith(LastRenderedBounds)?
            
                State = BaseItemState.Selected :

                State = BaseItemState.Unselected;

                return AspectConstants.EventResponse_Repaint;            
        }

        private object HandleNonLassoMouseMove(MousePluginEventArgs args)
        {
            if (State == BaseItemState.Dragged)
            {
                ContinueDrag(args.MousePosition);
            }

            if (args.IsInBounds)
            {
                if (args.MouseDown && _mouseDownOn)
                {
                    return TestForBeginDrag(args);
                }

                if (State == BaseItemState.Unselected)
                {
                    State = BaseItemState.Hovered;

                    return AspectConstants.EventResponse_Repaint;
                }                
            }
            else if (State == BaseItemState.Hovered)
            {
                State = BaseItemState.Unselected;

                return AspectConstants.EventResponse_Repaint;
            }

            return AspectConstants.EventResponse_Null;
        }

        private object TestForBeginDrag(MousePluginEventArgs args)
        {
            if (!args.MouseDown) return AspectConstants.EventResponse_Null;

            float xOffset = Math.Abs(LastMouseDownPoint.X - args.MousePosition.X);
            float yOffset = Math.Abs(LastMouseDownPoint.Y - args.MousePosition.Y);

            if (xOffset < .5f && yOffset < .5f)
            {
                return AspectConstants.EventResponse_Null;
            }

            BeginDrag();

            return AspectConstants.EventResponse_Repaint;
        }

        public PropertyDictionary Persist()
        {
            PropertyDictionary propertyDictionary = new PropertyDictionary();

            propertyDictionary.Set("position-x", Position.X);
            propertyDictionary.Set("position-y", Position.Y);

            propertyDictionary.Set("state", State == BaseItemState.Selected ? BaseItemState.Selected : BaseItemState.Unselected);

            return propertyDictionary;
        }

        public void Load(PropertyDictionary dictionary)
        {
            if (!dictionary.HasKey("position-x")) ExceptionBuilder.Format("Position-x not located in State Handler load");
            if (!dictionary.HasKey("position-y")) ExceptionBuilder.Format("Position-y not located in State Handler load");

            if (!dictionary.HasKey("state")) ExceptionBuilder.Format("State not located in State Handler load");

            Position = new PointF(dictionary.FloatFor("position-x"), dictionary.FloatFor("position-y"));
            State = dictionary.StringFor("state");
        }
    }

    public static class BaseItemState 
    {
		// Fields (4) 

        public const string Dragged = "dragged";
        public const string Hovered = "hovered";
        public const string Selected = "selected";
        public const string Unselected = "unselected";
    }
}
